package cn.xuqiudong.common.blog.search.helper;

import cn.xuqiudong.common.base.model.PageInfo;
import cn.xuqiudong.common.blog.search.lookup.SearchLookup;
import cn.xuqiudong.common.blog.search.model.SearchBlogArticleModel;
import cn.xuqiudong.common.util.JsonUtil;
import cn.xuqiudong.common.util.encrypt.Encodes;
import com.meilisearch.sdk.Client;
import com.meilisearch.sdk.Index;
import com.meilisearch.sdk.SearchRequest;
import com.meilisearch.sdk.exceptions.MeilisearchCommunicationException;
import com.meilisearch.sdk.model.SearchResult;
import com.meilisearch.sdk.model.Settings;
import com.meilisearch.sdk.model.TaskInfo;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 描述:
 *
 * @author Vic.xu
 * @since 2024-06-07 11:49
 */
public class SearchBlogHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(SearchBlogHelper.class);

    private static final String PRIMARY_KEY = "id";

    private Index index;


    private final String indexName;

    private final Client client;

    private boolean enable;

    /**
     * 构建index，不存在则创建
     */
    public SearchBlogHelper(boolean enable, Client client, String indexName) {
        this.enable = enable;
        this.client = client;
        this.indexName = indexName;
        try {
            client.getIndex(indexName);
        }catch (MeilisearchCommunicationException e){
            LOGGER.error(e.getMessage());
            LOGGER.warn("无法连接meilisearch, 不启用全文检索功能");
            this.enable = false;
            return ;
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
            LOGGER.info("索引不存在，开始创建{}", indexName);
            client.createIndex(indexName, PRIMARY_KEY);
        }
        index = client.index(indexName);
        Settings settings = new Settings();
        settings.setFilterableAttributes(new String[]{"id", "author", "categoryName", "tagNames"});
        settings.setSearchableAttributes(new String[]{"title", "content"});
        index.updateSettings(settings);
    }

    public Index getIndex(){
        return index;
    }

    public boolean isEnable() {
        return enable;
    }

    /**
     * add or replace documents
     * https://www.meilisearch.com/docs/reference/api/documents#add-or-replace-documents
     */
    public TaskInfo addDocument(SearchBlogArticleModel model) {
        if (model == null) {
            return null;
        }
        // 对内容进行html转义
        String content = model.getContent();
        content = Encodes.escapeHtml(content);
        model.setContent(content);
        TaskInfo info = index.addDocuments(JsonUtil.toJson(model));
        LOGGER.info("add document:{}", JsonUtil.toJson(info));
        return info;
    }


    public TaskInfo save(SearchBlogArticleModel searchBlogArticleModel) {
        return this.addDocument(searchBlogArticleModel);
    }

    /**
     * https://www.meilisearch.com/docs/reference/api/documents#delete-one-document
     */
    public TaskInfo delete(String id) {
        if (id == null) {
            return null;
        }
        TaskInfo taskInfo = index.deleteDocument(id);
        LOGGER.info("delete document:{}", JsonUtil.toJson(taskInfo));
        return taskInfo;
    }

    /**
     * https://www.meilisearch.com/docs/reference/api/documents#delete-documents-by-batch
     * https://www.meilisearch.com/docs/learn/fine_tuning_results/filtering#in
     */
    public TaskInfo deleteAll(List<String> idList) {
        if (CollectionUtils.isEmpty(idList)) {
            return null;
        }

//        TaskInfo taskInfo = index.deleteDocuments(idList);
        //形如 id IN [1, 2, 3]
        String filter = " id IN [" + idList.stream()
                .collect(Collectors.joining(", ")) + "] ";
        TaskInfo taskInfo = index.deleteDocumentsByFilter(filter);
        LOGGER.info("deleteAll document:{}", JsonUtil.toJson(taskInfo));
        index.waitForTask(taskInfo.getTaskUid());
        return taskInfo;
    }

    /**
     * 指定搜索和高亮的字段
     */
    private static final String[] FIELDS = new String[]{"title", "content", "categoryName", "tagNames", "topicName"};

    /**
     * https://www.meilisearch.com/docs/reference/api/search
     */
    public PageInfo<SearchBlogArticleModel> search(SearchLookup<SearchBlogArticleModel> lookup) {
        SearchRequest request = SearchRequest.builder().build()
                .setQ(lookup.getKeyword())
                .setOffset(lookup.getIndex()).setLimit(lookup.getSize())
                //搜索指定字段
                .setAttributesToRetrieve(new String[]{"*"})
                //指定高亮字段
                .setAttributesToHighlight(FIELDS)
                .setHighlightPreTag("<strong class='highlight'>")
                .setHighlightPostTag("</strong>")
                //只返回指定的字段
                //.setAttributesToRetrieve(new String[]{"title", "content"})
                //截取搜索结果 关键字前后的文本长度 content
                .setAttributesToCrop(new String[]{"content"})
                .setCropLength(40)
                ;
        SearchResult search = (SearchResult) index.search(request);
        List<SearchBlogArticleModel> list = new ArrayList<>();
        search.getHits().forEach(hit -> {
            Map<String, Object> formatted = (Map<String, Object>) hit.get("_formatted");
            SearchBlogArticleModel model = new SearchBlogArticleModel();
            try {
                BeanUtils.populate(model, formatted);
                list.add(model);
            } catch (Exception e) {
                LOGGER.error(e.getMessage(), e);
            }

        });
        return PageInfo.instance(search.getEstimatedTotalHits(), list, lookup);
    }

    public static void main(String[] args) {
      /*
          curl -X POST 'http://localhost:7700/indexes/articles/documents/delete-batch' \
      -H 'Content-Type: application/json' \
      -H 'Authorization: Bearer xuqiudong' \
      --data '{
        "filter": "id IN [\"id1\", \"id2\", \"id3\"]"
      }'
       */
    }
}
